package org.sninwo.lnbook.book.service;

import java.util.List;

import javax.annotation.Resource;

import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.sninwo.lnbook.book.dao.BookTypeDao;
import org.sninwo.lnbook.book.entity.Book;
import org.sninwo.lnbook.uitls.LangUtils;
import org.sninwo.lnbook.uitls.LnBookUtils;
import org.sninwo.lnbook.uitls.ReflectionUtils;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

@Component
@Transactional
public class BookImportManager {
	static final Logger logger = LoggerFactory.getLogger(BookImportManager.class);
	
	private static int MAX_MARK_LEN = 5;
	
	/**
	 * 定义书籍标记
	 */
	private BookMark[] bookMarks = {
		new BookMark("type", "类别"),
		new BookMark("name", "标题"),
		new BookMark("name2", "又名", new ValueFilter(){
			public String filter(String str){ //过滤又名的书名号
				return str.replaceFirst("^又名","").replace("《", "").replace("》", "");
			}
		}),
		new BookMark("author", "作者"),
		new BookMark("source", "出处", new ValueFilter(){
			public String filter(String str){
				return str.replaceFirst("^见", "");
			}
		}),
		new BookMark("rolls", "卷次"),
		new BookMark("aboutAuthor", "简介"),
		new BookMark("abstract_", "提要"),
		new BookMark("academicFeatures", "特点"),
		new BookMark("preface", "序跋"),
		new BookMark("chorography", "地方志"),
		new BookMark("holdings", "馆藏"),
		new BookMark("comment", "按语")
	};
	
	@Resource private BookTypeDao bookTypeDao;
	@Resource private BookManager bookMgr;
	
	/**
	 * 从电子版中导入。<br>
	 * 包括类别的添加，如果书籍的标题和作者一样则更新原有的记录。<br>
	 * 算法简介： 逐行检查，发现标记时，记住这个标记，并处理上一次标记的内容。
	 * @param lines
	 */
	public String doImport(List<String> lines){
		//在最后添加一个辅助的类别标记，方便添加最后一本书
		lines.add(getBookTypeMark().mark);
		//定义临时变量
		BookMark lastBookMark = null;
		Long curBookTypeId = null;
		Book book = null;
		StringBuilder tempValue = new StringBuilder();
		int typeCount = 0;
		int bookCount = 0;
		//逐行检查
		for (int i = 0; i < lines.size(); i ++){
			String line = LangUtils.trimCn(lines.get(i));//去掉空白，包括中文空格
			if (line.length() == 0){
				continue;
			}
			if (line.length() <= MAX_MARK_LEN){ //有可能是标记
				String mark = line.replace(":", "").replace("：", "");
				BookMark bookMark = findBookMark(mark);
				if (bookMark != null){ //发现标记
					//结束上一次的标记
					if (lastBookMark != null){
						String value = tempValue.toString().trim();
						if (value.length() > 0 && lastBookMark.filter != null){ //是否要过滤字符
							value = lastBookMark.filter.filter(value);
						}
						if (getBookTypeMark().attrName.equals(lastBookMark.attrName)){ //上一次的标记为类别
							if (StringUtils.isBlank(value)){
								LnBookUtils.throwError("发现“类别”标记，但是内容为空，行号是%s", i + 1);
							}
							//从数据库读取或者保存类别，并记住类别id
							curBookTypeId = bookTypeDao.getOtherwiseAdd(value).getId();
							typeCount ++;
							logger.debug("导入一个类别，名称为：{}", value);
						}
						else if(lastBookMark.attrName != null){//上一次的标记为书籍
							if (book == null){
								if (curBookTypeId == null){
									LnBookUtils.throwError("发现书籍没有类别，行号大概是%s", i + 1);
								}
								book = new Book();
								book.setTypeId(curBookTypeId);
							}
							//添加书的属性（上一次标记的属性）
							ReflectionUtils.setFieldValue(book, lastBookMark.attrName, value);
							//如果是书籍的结束则添加书籍
							if (isLastBookEnd(mark)){
								if (StringUtils.isNotBlank(book.getName())){
									bookMgr.saveUniqueBook(book);
									bookCount ++;
									logger.debug("导入一本书籍，名称为：{}", book.getName());
								}
								else{
									logger.warn("导入书籍时发现空书名，该书被忽略，行号大概是:{}", i);
								}
								book = null;
							}
						}
					}
					//记住新标记
					lastBookMark = new BookMark(bookMark.attrName, null, bookMark.filter);
					tempValue = new StringBuilder();
					continue;
				}
			}
			if (tempValue != null){
				tempValue.append(line).append("\n"); //添加属性值的内容
			}
		}
		return String.format("已成功导入类别%s个，书籍%s本", typeCount, bookCount);
	}
	
	/**
	 * 通过标记名称，在书籍标记数组中查找书籍标记
	 */
	private BookMark findBookMark(String mark){
		for (BookMark bookMark : bookMarks){
			if (bookMark.mark.equals(mark)){
				return bookMark;
			}
		}
		return null;
	}
	
	/**
	 * 判断是否到了书籍的结束标记。
	 * 当发现标记“类别”或“标题”时，表示上一次的书籍结束。
	 * @param mark
	 * @return
	 */
	private boolean isLastBookEnd(String mark){
		return (mark.equals(bookMarks[0].mark) ||
				mark.equals(bookMarks[1].mark));
	}
	
	private BookMark getBookTypeMark(){
		return bookMarks[0];
	}
	
	/**
	 * 电子书的标记
	 * attrName是Book的属性名
	 * mark是电子书中对应的标记
	 * filter用于字符串过滤
	 */
	private class BookMark{
		public String attrName;
		public String mark;
		public ValueFilter filter;
		
		/**
		 * 电子书的标记
		 * @param attrName 是Book的属性名
		 * @param mark 电子书中对应的标记
		 */
		public BookMark(String attrName, String mark){
			this.attrName = attrName;
			this.mark = mark;
		}

		/**
		 * 电子书的标记
		 * @param attrName 是Book的属性名
		 * @param mark 电子书中对应的标记
		 * @param mark 用于字符串过滤
		 */
		public BookMark(String attrName, String mark, ValueFilter filter){
			this.attrName = attrName;
			this.mark = mark;
			this.filter = filter;
		}
	}
	
	private interface ValueFilter{
		public String filter(String str);
	}
}
